package soundManager 
{
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.IOErrorEvent;
	import flash.events.ProgressEvent;
	import flash.media.ID3Info;
	import flash.media.Sound;
	import flash.media.SoundChannel;
	import flash.media.SoundLoaderContext;
	import flash.media.SoundTransform;
	import flash.net.URLRequest;

	/**
	 * ...
	 * @author SChabanov
	 */
	public final class SoundItem extends EventDispatcher implements ISoundItem
	{
		
		public static const DESTROYED:String = "destroyed";
		public static const STREAM_COMPLETE:String = "streamComplete";
				
		private var _channel:SoundChannel;
		private var _sound:Sound;
	
		private var _percentsLoaded:Number;
		
		private var _source:Object;
				
		private var _isLoaded:Boolean = false;
		private var _isPaused:Boolean = false;
		private var _isPlaying:Boolean = false;
		private var _isMuted:Boolean;
		
		private var _id3Info:ID3Info;
		
		private var _bufferTime:Number = 1000;
		private var _position:int;
		private var _loops:int;
		
		private var _soundId:String;
		
		private var _soundType:uint;
		
		private var _balance:Number = 0;
		private var _volume:Number =  1;
		private var _lastVolume:Number = 1;
		
		private var _checkPolicy:Boolean;
		private var _stream:Array;
		
							
		[Event(name = "complete", type = "flash.events.Event")]
		[Event(name = "ioError", type = "flash.events.IOErrorEvent")]
		[Event(name = "progress", type = "flash.events.ProgressEvent")]
		[Event(name = "id3", type = "flash.events.Event")]
		[Event(name = "soundComplete", type = "flash.events.Event")]
		
		[Event(name = "streamComplete", type = "soundManager.SoundItem")]
		[Event(name = "destroyed", type = "soundManager.SoundItem")]
		
		
		/**
		 * 
		 *
		 * @param	fileObject is Sound class instance
		 */
		public function SoundItem(fileObject:Object,soundId:String,buffer:Number=1000,checkPolicy:Boolean=false) 
		{
			this._checkPolicy = checkPolicy;
			this._soundId = soundId;
			this._bufferTime = buffer;
			this._source = fileObject;
			
			this._stream = [];
			
			if (fileObject is Class )
			{
				_sound = new fileObject();
				_isLoaded = true;
			}else if (fileObject is Sound) {
				
				_sound = fileObject as Sound;
				_isLoaded = _sound.bytesLoaded == _sound.bytesTotal;
			}else if (fileObject is String)
			{
				_sound = new Sound();
			}else {
				throw new Error("SoundItem : fileObject must be a class or string");
			}
	
			if (!_isLoaded)
			{
				_sound.addEventListener(Event.COMPLETE, onSoundLoaded, false, 0, true);
				//_sound.addEventListener(Event.OPEN, onSoundOpenHandler, false, 0, true);
				_sound.addEventListener(IOErrorEvent.IO_ERROR, onIoErrorHandler, false, 0, true);
				_sound.addEventListener(ProgressEvent.PROGRESS, onSoundProgressHandler, false, 0, true);
				_sound.addEventListener(Event.ID3, onGetID3Tags, false, 0, true);
			}else {
				_id3Info = _sound.id3;
			}
		
			
		}
		/*
		private function onSoundOpenHandler(e:Event):void 
		{
			trace("open")
		}*/
		
		private function onGetID3Tags(e:Event):void 
		{
			_id3Info = _sound.id3;
			dispatchEvent(e);
		}
		
		private function onSoundLoaded(e:Event):void 
		{
			_sound.removeEventListener(Event.COMPLETE, onSoundLoaded);
			_sound.removeEventListener(ProgressEvent.PROGRESS, onSoundProgressHandler);
			_sound.removeEventListener(IOErrorEvent.IO_ERROR, onIoErrorHandler);
			_sound.removeEventListener(Event.ID3, onGetID3Tags);
			_isLoaded = true;
			dispatchEvent(e);
		}
		
		
		private function onIoErrorHandler(e:IOErrorEvent):void 
		{
			//trace(e.text)
			clearStreams();
			dispatchEvent(e);
		}
		
		private function onSoundProgressHandler(e:ProgressEvent):void 
		{
			_percentsLoaded = (e.bytesLoaded / e.bytesTotal) * 100;
			dispatchEvent(e);
		}
		private function onSoundFinishPlay(e:Event):void 
		{
			stop();		
			dispatchEvent(e);
		}
		
		public function destroy():void
		{
			if(_sound.isBuffering)_sound.close();
			_sound.removeEventListener(Event.COMPLETE, onSoundLoaded);
			_sound.removeEventListener(ProgressEvent.PROGRESS, onSoundProgressHandler);
			_sound.removeEventListener(IOErrorEvent.IO_ERROR, onIoErrorHandler);
			_sound.removeEventListener(Event.ID3, onGetID3Tags);
			_sound = null;
			stop();
			clearStreams();
			delete this;
			dispatchEvent(new Event(SoundItem.DESTROYED));
		}
		
		public function initPlayParams(position:Number=0,loops:int=0,volume:Number=1):void
		{
			this._volume = volume;
			this._loops = loops;
			this._position = position;
			
		}
		
		public function play():void
		{
			if (_isMuted || _channel) return;
			
			_isPlaying = true;
			_isPaused = false;
			try
			{
				loadSoundFile();
				_channel = _sound.play(_position, _loops,new SoundTransform(_volume,_balance));
				_channel.addEventListener(Event.SOUND_COMPLETE, onSoundFinishPlay, false, 0, true);
			}catch (e:Error) {
				Logger.globalLog(e.getStackTrace(),true);
			}
			
		}
		
		/**
		 * for many sound play 
		 * @param	pos
		 * @param	volume
		 */
		public function playStream(pos:Number=0,volume:Number=1,balance:Number=0):void
		{
			if (_isMuted) return;
			try
			{
				loadSoundFile();
				var ch:SoundChannel = _sound.play(_position, 0, new SoundTransform(volume, balance));
				ch.addEventListener(Event.SOUND_COMPLETE, onStreamFinishPlay, false, 0, true);
				_stream.push(ch);
			}catch (e:Error) {
				Logger.globalLog(e.getStackTrace(),true);
			}
			
		}
		
		private function loadSoundFile():void 
		{
			try
			{
				if (!_isLoaded)
				{
					_sound.load(new URLRequest(_source.toString()), new SoundLoaderContext(_bufferTime,_checkPolicy))
				}
			}catch (e:Error) {
				Logger.globalLog(e.getStackTrace(),true);
			}
			
		}
		
		private function onStreamFinishPlay(e:Event):void 
		{
			var ch:SoundChannel = e.currentTarget as SoundChannel;
			ch.removeEventListener(e.type, onStreamFinishPlay);
			_stream.splice(_stream.indexOf(ch), 1);
			ch = null;
			dispatchEvent(new Event(SoundItem.STREAM_COMPLETE));
			//trace("stream",_stream.length);
		}
		
		private function clearStreams():void
		{
			while (_stream.length > 0)
			{
				var ch:SoundChannel = _stream.pop();
				ch.stop();
				ch.removeEventListener(Event.SOUND_COMPLETE, onStreamFinishPlay);
				ch = null;
			}
			
		}
		
		public function stop():void
		{		
			
			if (_channel)
			{
				_isPaused = false;
				_isPlaying = false;
				_channel.stop();
				_channel.removeEventListener(Event.SOUND_COMPLETE, onSoundFinishPlay);
				_position = _channel.position;
				_channel = null;
			}
			
		}
		
		public function pause():void
		{
			_isPaused = true;
			_isPlaying = false;
			if (_channel)
			{
				_position = _channel.position;
				_channel.stop()
				_channel.removeEventListener(Event.SOUND_COMPLETE, onSoundFinishPlay);
				_channel = null;
			}
		}
		/**
		 * 
		 * @param	value -1,0,1 - left center right
		 */
		public function set balance(value:Number):void
		{
			_balance = value;
			if (_channel)
			{
				var snd:SoundTransform = _channel.soundTransform;
				snd.pan = _balance;
				_channel.soundTransform = snd;
				
			}
			
		}
		/**
		 * 
		 */
		public function get balance():Number
		{
			return _balance;
		}
		/**
		 * @param value 0-1
		 */
		public function set volume(value:Number):void
		{
			_volume = value;
			if (_channel && !_isMuted)
			{
				var snd:SoundTransform = _channel.soundTransform;
				snd.volume = _volume;
				_channel.soundTransform = snd;
			}
			
		}
		/**
		 * 
		 */
		public function get volume():Number
		{
			return _volume;
		}
		/**
		 * @return length of sound in milliseconds;
		 */
		public function get duration():Number
		{
			return _sound.length;
		}
		public function get currentPosition():Number
		{
			if (_channel)_position = _channel.position;
			return _position;
		}
		public function get percentsLoaded():Number 
		{
			return _percentsLoaded;
		}
		public function get isPlaying():Boolean 
		{
			return _isPlaying;
		}
		public function get soundType():uint 
		{
			return _soundType;
		}
		public function set soundType(value:uint):void 
		{
			_soundType = value;
		}
		public function get soundId():String 
		{
			return _soundId;
		}		
		public function get isPaused():Boolean 
		{
			return _isPaused;
		}
		public function get isLoaded():Boolean 
		{
			return _isLoaded;
		}
		public function set mute(value:Boolean):void
		{
			_isMuted = value;
			if (_channel)
			{
				var transform:SoundTransform;
				if (value)
				{
					transform = _channel.soundTransform;
					transform.volume = 0;
					_channel.soundTransform = transform;
				}else {
					transform = _channel.soundTransform;
					transform.volume = _volume;
					_channel.soundTransform = transform;
				}
			}
		}
		
		
							
	}

}